﻿using System;
using System.Collections.Generic;

using System.Text;
using GGSharp.Packets;
using System.Threading;
using System.IO;

namespace GGSharp
{
    public class GGSender : IDisposable
    {
        protected const int SLEEPTIME = 250;
        protected const int PINGTIMEOUT = 180000;

        public delegate void PacketSentDelegate(object sender, HeaderPacket packet);
        public event PacketSentDelegate PacketSent;

        private Queue<HeaderPacket> _packetQueue = new Queue<HeaderPacket>();
        private GGConnection _connection;
        private Thread _thread;
        private Timer _connectionAliveTimer;
        private BinaryWriter _writer;

        public GGSender(GGConnection connection)
        {
            _connection = connection;
            _thread = new Thread(new ThreadStart(checkAndProcessQueue));   // towrzymy nowy watek
            _thread.IsBackground = true;
            _connection.Connected += _connection_Connected;

            _connectionAliveTimer = new Timer(new TimerCallback(_connectionAlive_Timeout), null, 0, 0);
        }

        void _connectionAlive_Timeout(object param)
        {
            Send(PingPacket.Default);
        }

        void _connection_Connected(object sender, EventArgs e)
        {
            _packetQueue.Clear();
            _writer = new BinaryWriter(_connection.Stream);
            _thread.Start(); // uruchamiamy watek
            _connectionAliveTimer.Change(PINGTIMEOUT, PINGTIMEOUT);
            _connection.Connected -= _connection_Connected;
            _connection.Disconnected += _connection_Disconnected;
        }

        void _connection_Disconnected(object sender, EventArgs e)
        {
            _thread.Abort();
            _writer = null;
            _connectionAliveTimer.Change(0, 0);
            _connection.Connected += _connection_Connected;
            _connection.Disconnected -= _connection_Disconnected;
        }

        public virtual void OnPackedSent(HeaderPacket packet)
        {
            if (PacketSent != null)
                PacketSent.BeginInvoke(this, packet, null, null);
        }

        private void checkAndProcessQueue()
        {
            try
            {
                while (true)
                {
                    //jesli NIE jest dostepny header to czekaj dalej
                    if (!_connection.IsConnected || _packetQueue.Count == 0)
                    {
                        Thread.Sleep(SLEEPTIME);
                        continue;
                    }

                    HeaderPacket packet = _packetQueue.Dequeue();
                    packet.Write(_writer);
                    OnPackedSent(packet);
                }
            }
            catch (ThreadAbortException e) { }
            catch (Exception e) { _connection.Disconnect(); }
        }

        public void Send(Packets.HeaderPacket packet)
        {
            _packetQueue.Enqueue(packet);
        }

        #region IDisposable Members

        public void Dispose()
        {
            _connection = null;
        }

        #endregion
    }
}
